home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / standards / sgml / nist / parse3 / didriver.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-09-13  |  32.6 KB  |  860 lines

  1. /* National Institute of Standards and Technology (NIST)
  2. /* National Computer System Laboratory (NCSL)
  3. /* Office Systems Engineering (OSE) Group
  4. /* ********************************************************************
  5. /*                            D I S C L A I M E R
  6. /*                              (March 8, 1989)
  7. /*  
  8. /* There is no warranty for the NIST NCSL OSE SGML parser and/or the NIST
  9. /* NCSL OSE SGML parser validation suite.  If the SGML parser and/or
  10. /* validation suite is modified by someone else and passed on, NIST wants
  11. /* the parser's recipients to know that what they have is not what NIST
  12. /* distributed, so that any problems introduced by others will not
  13. /* reflect on our reputation.
  14. /* 
  15. /* Policies
  16. /* 
  17. /* 1. Anyone may copy and distribute verbatim copies of the SGML source
  18. /* code as received in any medium.
  19. /* 
  20. /* 2. Anyone may modify your copy or copies of SGML parser source code or
  21. /* any portion of it, and copy and distribute such modifications provided
  22. /* that all modifications are clearly associated with the entity that
  23. /* performs the modifications.
  24. /* 
  25. /* NO WARRANTY
  26. /* ===========
  27. /* 
  28. /* NIST PROVIDES ABSOLUTELY NO WARRANTY.  THE SGML PARSER AND VALIDATION
  29. /* SUITE ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
  30. /* EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  31. /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  32. /* THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS
  33. /* WITH YOU.  SHOULD THE SGML PARSER OR VALIDATION SUITE PROVE DEFECTIVE,
  34. /* YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
  35. /* 
  36. /* IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL NIST BE LIABLE FOR
  37. /* DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR OTHER SPECIAL,
  38. /* INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
  39. /* INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
  40. /* BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A
  41. /* FAILURE OF THE PROGRAM TO OPERATE WITH PROGRAMS NOT DISTRIBUTED BY
  42. /* NIST) THE PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF
  43. /* SUCH DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
  44. */
  45.  
  46. /*****************************************************************************/
  47. /*      TITLE:          SGML Parser                                          */
  48. /*      SYSTEM:         Document Processor                                   */
  49. /*      SUBSYSTEM:                                                           */
  50. /*      SOURCE FILE:    DIDRIVER.C                                           */
  51. /*      AUTHOR:         Steve Lindeman, Fred Maples                          */
  52. /*                                                                           */
  53. /*      This is a subsystem of a Standard Generalized Markup Language        */
  54. /*      (SGML) parser conforming to ISO 8879-1986(E).  This subsystem        */
  55. /*      will be referred to as the "instance parser" because it processes    */
  56. /*      the actual instance of the tagged document rather than the Document  */
  57. /*      Type Definition.  This parser assumes the Core Concrete Syntax       */
  58. /*      and the OMITTAG and FORMAL features of SGML.                         */
  59. /*                                                                           */
  60. /*      First the command line options are read to determine what            */
  61. /*      functions of the parser are requested.  A full listing of options    */
  62. /*      are documented in 'readme.par', among other places.                  */
  63. /*                                                                           */
  64. /*      This parser can output an ASCII Text File defined to be a            */
  65. /*      Canonical Test Result (CTR) that could actually determine if the     */
  66. /*      parsing was done correctly.  SGML is an input only language.  There  */
  67. /*      are no requirements in the language that state what the output       */
  68. /*      of the parser must be.  It is required only to report an error if    */
  69. /*      one exists in the document, and not report an error if one does      */
  70. /*      not exist.  A description of the CTR language can also be found in   */
  71. /*      external documentation.                                              */
  72. /*                                                                           */
  73. /*      DATE CREATED:   July 13, 1987                                        */
  74. /*      LAST MODIFIED:                                                       */
  75. /*                                                                           */
  76. /*                     REVISIONS                                             */
  77. /*      WHEN      WHO            WHY                                         */
  78. /*                                                                           */
  79. /*****************************************************************************/
  80.  
  81. #include <stdio.h>
  82. #include "didefs.h"
  83. #include "diglobal.h"
  84.  
  85. main(argc,argv)
  86. int argc;
  87. char *argv[];
  88. {
  89.    STENTRY *tp;  /* pointer to symbol table entry */
  90.    TNODE *newcm;  /* ptr to root of newly created content model */
  91.    ENTITYDESC *genthead,*penthead;
  92.    ID_IDREF_DESC *idrefp;
  93.    char path[PATHLEN];
  94.    int token;
  95.    STATUS retval;
  96.    TKNRETVAL tknretval;
  97.    BOOLEAN root_minimized,delete_temps,bld_ctr;
  98.  
  99.    /*   The initialization of the instance-parser involves reading in a
  100.       hiearchical representation of what the instance of the document may
  101.       contain.  This tree-like structure is traversed as the execution of
  102.       the parser progresses.  There are always exceptions to the rules
  103.       (e.g. ANY declared content, exclusion or inclusion exceptions) that
  104.       may cause the tree traversals to look quite strange.  Once the entire
  105.       document is processed, control is returned to this main and a cross
  106.       reference is made of all the IDs and IDREFs made during the parse to
  107.       ensure there is an ID for every IDREF.  There is no requirement that
  108.       there be an IDREF for every ID. */
  109.  
  110.    checkopt(argc,argv,path,&delete_temps,&bld_ctr);
  111.    init(&numsym,&genthead,&penthead,path); /* build symtable and trees */
  112.    gettilnosep();  /* ??? */
  113.    tknretval = gettoken(&tp,&token,genthead,penthead,&dontcare);  /* get root token */
  114.    if (tknretval==MARKUP_FOUND && token!=rootid)
  115.       ungettoken(token,tp);
  116.    if (tknretval==TEXT || token!=rootid) {
  117.       root_minimized = TRUE;
  118.       tp = perform_roottag_mini(rootid);
  119.    }
  120.    else
  121.       root_minimized = FALSE;
  122.    newcm = pushcreate(tp);
  123.    if ((retval=traverse(newcm,tp,genthead,penthead,&dontcare)) == NFSH) {
  124.       if (tknretval == TEXT)
  125.          sprintf(error_msg,"%s%s%s","\nError: Invalid data, last opened element '",tp->nametoken,"'.\n");
  126.       else
  127.          sprintf(error_msg,"%s%s%s","\nError: Invalid tag, last opened element '",tp->nametoken,"'.\n");
  128.       FATAL_ERROR()
  129.    }
  130.    /* check to make sure the element has content */
  131.    if (retval==NFDHT && root_minimized) {
  132.       sprintf(error_msg,"%s'%s'.\n","\nError: Invalid Starttag Minimization on tag ",tp->nametoken);
  133.       FATAL_ERROR()
  134.    }
  135.    if (EMPTY_CONTENT(newcm)) {  /* can't have endtag for EMPTY */
  136.       putstr_outbuf("\n[/");
  137.       putstr_outbuf(symtable[rootid].nametoken);
  138.       place_in_queue(END_TAG_NAME,symtable[rootid].nametoken,"");
  139.       putstr_outbuf("]");
  140.       token = rootid | HIGHBIT;   /* don't change tp */
  141.       tknretval = MARKUP_FOUND;
  142.    }
  143.    else
  144.       tknretval = gettoken(&tp,&token,genthead,penthead,&dontcare);  /* must be end tag */
  145.    if (tknretval==TEXT || IS_STARTTAG(token) || IS_ENDTAG_NOTEQ(token,rootid))
  146.       resolve_endtag((TNODE *) NULL,token,tp,&retval,tknretval,genthead,penthead,rootid);
  147.    flush_buf();
  148.    popfree(newcm);  /* through with this content model */
  149.    if ((idrefp=cross_id_idref(idhead,idrefhead)) != NULL) {
  150.       sprintf(error_msg,"%s'%s'.\n","\nError: Unsuccessful crossref for ID-IDREF ",idrefp->name);
  151.       FATAL_ERROR()
  152.    }
  153.    gettilnosep();
  154.    if (our_fgetc(indoc) != EOF)
  155.       ourexit(2,"\nInvalid additional text in document.\n");
  156.    if (fclose(indoc) != 0) {
  157.       printf("Unable to close input document.\n");
  158.       exit(99);
  159.    }
  160.    if (bld_ctr)
  161.       if (fclose(ctrfp) != 0) {
  162.          printf("Unable to close CTR file.\n");
  163.          exit(99);
  164.       }
  165.    if (delete_temps)
  166.       delete_files(path);
  167.    printf("\nNormal program termination.\n");
  168. }
  169.  
  170. /*------------------------------------------------------*/
  171. /*             D O O P E R A T O R        */
  172. /*       This routine processes a tree node that   */
  173. /*       contains an operator.  However, we don't  */
  174. /*  which operator yet.          */
  175. /*                   */
  176. /*          returns -- NFDHT, FOUND, NFSH    */
  177. /*------------------------------------------------------*/
  178. STATUS dooperator(ptr,tp,genthead,penthead)
  179. TNODE *ptr;
  180. STENTRY *tp;
  181. ENTITYDESC *genthead,*penthead;
  182. {
  183.    STATUS retval,prevretval;
  184.  
  185.    prevretval = NFDHT;  /* anything but FOUND */
  186.    while(testoi(ptr) != OI_IS_NULL) {
  187.       switch(ptr->nodeid) {
  188.       case COMMA:
  189.          retval = exe_seq(ptr,tp,genthead,penthead);
  190.          break;
  191.       case OR:
  192.          retval = exe_or(ptr,tp,genthead,penthead);
  193.          break;
  194.       case AND:
  195.          retval = exe_and(ptr,tp,genthead,penthead);
  196.          break;
  197.       }
  198.  
  199.       /*  Once you've found one occurrence of that token, an attempt to find
  200.          another must be thought of as FOUND and not NFDHT.  If it was mis-
  201.          interpreted as NFDHT, the parsing might incorrectly attempt to parse
  202.          right-hand side of an OR group. */
  203.  
  204.       if ((prevretval==FOUND) && (retval==NFDHT))
  205.          retval = FOUND;
  206.       prevretval = retval;
  207.    }
  208.    return(retval);
  209. }
  210.  
  211. /*------------------------------------------------------*/
  212. /*         D O O P E R A N D        */
  213. /*       This content model is not fully defined   */
  214. /*       here; there is another content model that */
  215. /*       would logically be a child of this one.   */
  216. /*                        */
  217. /*       returns -- NFDHT, NFSH, FOUND          */
  218. /*------------------------------------------------------*/
  219. STATUS dooperand(ptr,tp,genthead,penthead)
  220. TNODE *ptr;
  221. STENTRY *tp;
  222. ENTITYDESC *genthead,*penthead;
  223. {
  224. /* Correction to problem reported by LES GONDOR */
  225.    STATUS retval = NFDHT;
  226.    int token;
  227.    TKNRETVAL tknretval;
  228.  
  229.    while (testoi(ptr) != OI_IS_NULL) {   /* stay down in tree til node's resolved */
  230.       tknretval = gettoken(&tp,&token,genthead,penthead,&dontcare);
  231.       if (tknretval == TEXT) 
  232.          resolve_starttag(ptr,&retval,genthead,penthead,tknretval,token);
  233.       else
  234.          if (ptr->nodeid == token && !find_except(currexcl,token))  /* token is starttag */
  235.             retval = exe_new_conmod(tp,genthead,penthead,ptr,FALSE);
  236.          else {
  237.             ungettoken(token,tp);   /* no match, assume needed elsewhere */
  238.             resolve_starttag(ptr,&retval,genthead,penthead,tknretval,token);
  239.          }
  240.    }  /* end while */
  241.    return(retval);
  242. }
  243.  
  244. /*------------------------------------------------------*/
  245. /*         D O T E R M I N A L         */
  246. /*           This content model has been defined      */
  247. /*           down to it's lowest level possible    */
  248. /*                   */
  249. /*        returns -- NFDHT, FOUND, NFSH      */
  250. /*------------------------------------------------------*/
  251. STATUS doterminal(ptr,genthead,penthead)
  252. TNODE *ptr;
  253. ENTITYDESC *genthead,*penthead;
  254. {
  255.    register STATUS retval;
  256.  
  257.    /* If an attempt to get a token (a tag) has been made and it was not
  258.       successfully matched as a valid token in the tree, it can be "put
  259.       back" and re-read later.  If a token has been put back, an attempt to
  260.       read data should be thought of as NFDHT in order to keep from
  261.       incorrectly reading up characters of data. */
  262.  
  263.    if (state == GETNEW)
  264.       switch(ptr->nodeid) {
  265.       case CDATA:
  266.          retval = getcdata();
  267.          break;
  268.       case RCDATA:
  269.          retval = getrcdata(genthead,TRUE,&dontcare,TRUE);
  270.          break;
  271.       case PCDATA:
  272.          retval = getpcdata(genthead,penthead);
  273.          break;
  274.       case EMPTY:
  275.          /* fix from Fred */
  276.          retval = FOUND;
  277.          break;
  278.       default:
  279.          software_fault();
  280.       }
  281.    else
  282.       retval = NFDHT;  /* if state=GETOLD don't try to get data */
  283.    return(retval);
  284. }
  285.  
  286. /*------------------------------------------------------*/
  287. /*            E X E _ N E W _ C O N M O D               */
  288. /*      This routine is executed when a new content   */
  289. /*      model has been encountered and thus the parse    */
  290. /*      needs to continue accordingly.  A pointer to  */
  291. /*      token is pushed on the stack, and parse    */
  292. /* begins traversing on the newly created content  */
  293. /* model.                  */
  294. /*------------------------------------------------------*/
  295. STATUS exe_new_conmod(tp,genthead,penthead,ptr,perform_strttag)
  296. STENTRY *tp;
  297. ENTITYDESC *genthead,*penthead;
  298. TNODE *ptr;
  299. BOOLEAN perform_strttag;
  300. {
  301.    int token;
  302.    TKNRETVAL tknretval;
  303.    STATUS retval;
  304.    TNODE *newcm;
  305.  
  306.    /*  must create a new content model to allow recursive 
  307.           definitions. then go traverse on the new content model */
  308.    newcm = pushcreate(tp);
  309.    if ((retval=traverse(newcm,tp,genthead,penthead,&dontcare)) == NFSH) {
  310.       if (tknretval == TEXT)
  311.          sprintf(error_msg,"%s%s%s","\nError: Invalid data, last opened element '",tp->nametoken,"'.\n");
  312.       else
  313.          sprintf(error_msg,"%s%s%s","\nError: Invalid tag, last opened element '",tp->nametoken,"'.\n");
  314.       FATAL_ERROR()
  315.    }
  316.    else
  317.       /* It is an error to have done start-tag minimization on an element
  318.          that contains empty content. */
  319.       if (retval==NFDHT && perform_strttag==TRUE) {
  320.          sprintf(error_msg,"%s%s%s","\nError: Invalid Starttag Minimization on tag '",tp->nametoken,"'.\n");
  321.          FATAL_ERROR()
  322.       }
  323.       else {
  324.  
  325.          /*  This prints out the endtag for elements that are required to have
  326.             no endtag possibly through tag minimization, EMPTY declared content, 
  327.             or content reference attributes.  */
  328.          if (EMPTY_CONTENT(newcm)) {  /* can't have endtag for EMPTY */
  329.             putstr_outbuf("\n[/");
  330.             putstr_outbuf(tp->nametoken);
  331.             place_in_queue(END_TAG_NAME,tp->nametoken,"");
  332.             putstr_outbuf("]");
  333.             token = ptr->nodeid | HIGHBIT;   /* don't change tp */
  334.             tknretval = MARKUP_FOUND;
  335.          }
  336.          else
  337.             tknretval = gettoken(&tp,&token,genthead,penthead,&dontcare);
  338.  
  339.          /*  If you were looking for an endtag for an element and actually found
  340.             data instead, an attempt must be made to minimize the end tag.  The 
  341.             only thing involved in the attempt is verifying that endtag minimization
  342.             was declared as legal on its ELEMENT declaration.  */
  343.  
  344.          if (tknretval == TEXT)
  345.             resolve_endtag(ptr,token,tp,&retval,tknretval,genthead,penthead,newcm->nodeid);
  346.          else
  347.             if (IS_ENDTAG(token) && IS_ENDTAG_EQ(token,ptr->nodeid)) {
  348.                decroi(ptr);
  349.                retval = FOUND;
  350.             }
  351.             else
  352.                resolve_endtag(ptr,token,tp,&retval,tknretval,genthead,penthead,ptr->nodeid);
  353.       }
  354.    popfree(newcm);  /* through with this content model */
  355.    return(retval);
  356. }
  357.  
  358. /*------------------------------------------------------*/
  359. /*               E X E _ A N D         */
  360. /*      This routine processes the AND operator.   */
  361. /* ANDs are stored as circularly linked lists   */
  362. /* with each element in the list representing   */
  363. /* an element in the AND group.  Control keeps  */
  364. /*      circling the list until all are matched    */
  365. /*      or an entire circle was made and no match  */
  366. /*      was made.             */
  367. /*                   */
  368. /* returns -- FOUND, NFDHT, NFSH       */
  369. /*------------------------------------------------------*/
  370. STATUS exe_and(ptr,tp,genthead,penthead)
  371. TNODE *ptr;
  372. STENTRY *tp;
  373. ENTITYDESC *genthead,*penthead;
  374. {
  375.    unsigned num_ands,num_proc;
  376.    STATUS retval;
  377.    BOOLEAN foundone;
  378.    register TNODE *currp;
  379.  
  380.    /*  Count the number of tokens in the AND circular linked lists. */
  381.    num_ands = 1;
  382.    for (currp=ptr->u.llptr; currp->next!=ptr->u.llptr; currp=currp->next)
  383.       num_ands++;
  384.  
  385.    num_proc = 0;   
  386.    foundone = FALSE;
  387.    for (currp=ptr->u.llptr; num_ands>0 && num_ands!=num_proc; currp=currp->next)
  388.  
  389.       /*   This node will be visited if it has NOT already been satisfied.  The
  390.          occurrence indicator was set to OI_IS_NULL when it was satisfied.  The AND
  391.          parsing continues until all nodes are satisfied or you go all the way
  392.          around the list without satisfying any nodes. */
  393.  
  394.       if (testoi(currp) != OI_IS_NULL)
  395.          switch(retval=traverse(currp,tp,genthead,penthead,&dontcare)) {
  396.          case NFSH:  
  397.          case NFDHT:
  398.             num_proc++;
  399.             restoreoi(currp);
  400.             break;
  401.          case FOUND:
  402.             num_proc = 0;
  403.             num_ands--;
  404.             foundone = TRUE;
  405.             break;
  406.          }
  407.    if (num_ands == 0)
  408.       retval = FOUND;
  409.    else {
  410.       /* It could have been that some items in the AND group were satisfied
  411.          and some were not.  This determines what STATUS the entire AND group
  412.          should resolve to based on the priority:  FOUND, NFDHT, then NFSH
  413.          taking into account whether or not the nodes were satisfied. */
  414.  
  415.       retval = NFDHT;   /* assume NFDHT, until you find out different */
  416.  
  417.       for (currp=ptr->u.llptr; currp->next!=ptr->u.llptr; currp=currp->next)
  418.          solveand(&retval,currp,ptr);
  419.       solveand(&retval,currp,ptr);  /* don't forget about last one in list */
  420.    }  
  421.    switch(retval) {
  422.    case FOUND:
  423.       decroi(ptr); 
  424.       break;
  425.    case NFDHT:
  426.       NULLOI(ptr);
  427.       break;
  428.    case NFSH:
  429.       if (testoi(ptr)==OPT && !foundone)
  430.          retval = NFDHT;
  431.       NULLOI(ptr);
  432.       if (foundone) {   /* at least one was FOUND, at least one was NFSH */
  433.          sprintf(error_msg,"%s%s%s","\nError: Invalid or missing tag, last opened element '",tp->nametoken,"'.\n");
  434.          FATAL_ERROR()
  435.       }
  436.       break;
  437.    }
  438.  
  439.    /* restore everything in '&' list except '&' node */
  440.    if (testoi(ptr) != OI_IS_NULL) {
  441.       for (currp=ptr->u.llptr; currp->next!=ptr->u.llptr; currp=currp->next)
  442.          restoreoi(currp);
  443.       restoreoi(currp);
  444.    }
  445.    return(retval);
  446. }
  447.  
  448. /*--------------------------------------------------------------*/
  449. /*             E X E _ O R             */
  450. /* This routine processes an OR operator found in the */
  451. /*      content model tree.  If left side is satisfied, the */
  452. /* right side is not processed.  If the left side is not */
  453. /* satisfied, the right side must be.  OR operator means */
  454. /* "one or the other, but not both".  The OR operator */
  455. /* is both commutative and associative.   That is:    */
  456. /*    (A|B|C) == ((A|B)|C) == (A|(B|C))      */
  457. /* Thus we have implemented all OR groups as binary trees. */
  458. /*                      */
  459. /*        returns -- FOUND, NFDHT, NFSH         */
  460. /*--------------------------------------------------------------*/
  461. STATUS exe_or(ptr,tp,genthead,penthead)
  462. TNODE *ptr;
  463. STENTRY *tp;
  464. ENTITYDESC *genthead,*penthead;
  465. {
  466.    STATUS retval;
  467.  
  468.    switch(traverse(ptr->left,tp,genthead,penthead,&dontcare)) {
  469.       /*  If you went left in the OR group and found the token, you cannot
  470.          make an attempt to parse the right-hand side.  */
  471.    case FOUND:
  472.       decroi(ptr);
  473.       retval = FOUND;
  474.       if (testoi(ptr) != OI_IS_NULL) 
  475.          restoreoi(ptr->left);  /* restore only left, never went right */
  476.       break;
  477.       /*  If you went left in an OR group and didn't satisfy the token, you
  478.          MUST make an attempt to parse the right-hand side. */
  479.    case NFSH:  
  480.    case NFDHT:
  481.       switch(traverse(ptr->u.right,tp,genthead,penthead,&dontcare)) {
  482.       case FOUND:
  483.          retval = FOUND;
  484.          decroi(ptr);    /* found on right, decrement operator o.i */
  485.          if (testoi(ptr) != OI_IS_NULL) {
  486.             restoreoi(ptr->left);   /* rebuild left and right subtrees */
  487.             restoreoi(ptr->u.right);
  488.          }
  489.          break;
  490.       case NFDHT:
  491.          NULLOI(ptr);
  492.          retval = NFDHT;
  493.          break;
  494.       case NFSH:  /* not found left or right, see if node's optional */
  495.          retval = (testoi(ptr) == OPT) ? NFDHT : NFSH;
  496.          NULLOI(ptr);   /* cannot have anymore */
  497.          break;
  498.       }
  499.       break;
  500.    }
  501.    return(retval);
  502. }
  503.  
  504. /*--------------------------------------------------------------*/
  505. /*                 E X E _ S E Q          */
  506. /*       This routine processes an COMMA operator found in  */
  507. /*     the content model tree.  First the left side is   */
  508. /*  processed, then right side is   processed.  The SEQ  */
  509. /*  operator is associative but NOT commutative.      */
  510. /*  That is:  (A,B,C) == ((A,B),C) == (A,(B,C))    */
  511. /*  Thus we have implemented all SEQ groups as binary    */
  512. /*  trees.                    */
  513. /*                      */
  514. /*                returns -- FOUND, NFDHT, NFSH       */
  515. /*--------------------------------------------------------------*/
  516. STATUS exe_seq(ptr,tp,genthead,penthead)
  517. TNODE *ptr;
  518. STENTRY *tp;
  519. ENTITYDESC *genthead,*penthead;
  520. {   
  521.    register STATUS retval;
  522.  
  523.    switch(traverse(ptr->left,tp,genthead,penthead,&dontcare)) {
  524.    case FOUND:
  525.       change_right(ptr->u.right,FOUND);
  526.       switch(traverse(ptr->u.right,tp,genthead,penthead,&dontcare)) {
  527.       case NFDHT:  
  528.       case FOUND:    /* OK left and right */
  529.          retval = FOUND;
  530.          decroi(ptr);
  531.          if (testoi(ptr) != OI_IS_NULL) {  /* restore only if more can occur */
  532.             restoreoi(ptr->left);
  533.             restoreoi(ptr->u.right);
  534.          }
  535.          break;
  536.       default:
  537.          sprintf(error_msg,"%s%s%s","\nError: Invalid or missing tag, last opened element '",tp->nametoken,"'.\n");
  538.          FATAL_ERROR()
  539.             break;
  540.       }
  541.       break;
  542.    case NFSH:
  543.       /* not found left, if node OPT then ok, else assume error */
  544.       retval = (testoi(ptr) == OPT) ? NFDHT : NFSH;
  545.       NULLOI(ptr);
  546.       break;
  547.    case NFDHT:
  548.       change_right(ptr->u.right,NFDHT);
  549.       switch(traverse(ptr->u.right,tp,genthead,penthead,&dontcare)) {
  550.       case FOUND:
  551.          retval = FOUND;
  552.          decroi(ptr);    /* found on right, decrement operator o.i */
  553.          if (testoi(ptr) != OI_IS_NULL) {
  554.             restoreoi(ptr->left);   /* rebuild left and right subtrees */
  555.             restoreoi(ptr->u.right);
  556.          }
  557.          break;
  558.       case NFDHT:  /* NFDHT on both left and right, inherently optional */
  559.          NULLOI(ptr);
  560.          retval = NFDHT;
  561.          break;
  562.       case NFSH:
  563.          /* not found right, see if optional */
  564.          retval = (testoi(ptr) == OPT) ? NFDHT : NFSH;
  565.          NULLOI(ptr);
  566.          break;
  567.       }
  568.       break;
  569.    }
  570.    return(retval);
  571. }
  572.  
  573. /*------------------------------------------------------*/
  574. /*       R E S O L V E _ E N D T T A G _ M I N I        */
  575. /*      This routine attempts to perform endtag    */
  576. /*      minimization because a tag was encountered */
  577. /*      that was found in the symbol table but did */
  578. /*      not correspond to the expected tag.  In order */
  579. /* to minimize the tag, all that is required is */
  580. /*      that minimization was enabled on the DTD      */
  581. /*      element declaration.                          */
  582. /*------------------------------------------------------*/
  583. void resolve_endtag(ptr,token,tp,retval,tknretval,genthead,penthead,endtok)
  584. TNODE *ptr;
  585. int token;
  586. STENTRY *tp;
  587. STATUS *retval;
  588. TKNRETVAL tknretval;
  589. ENTITYDESC *genthead,*penthead;
  590. int endtok;
  591. {
  592.    int token2;
  593.    STENTRY *opened_tp;
  594.    TNODE *newcm;
  595.  
  596.    /* If the token that was read is a valid inclusion exception, you should
  597.       traverse the tree for that token as far as possible, then attempt to
  598.       resolve the missing endtag that got you here. */
  599.  
  600.    if (tknretval==MARKUP_FOUND && find_except(currincl,token) && !find_except(currexcl,token)) {
  601.       newcm = pushcreate(tp);
  602.       if ((*retval = traverse(newcm,tp,genthead,penthead,&dontcare)) == NFSH) {
  603.          if (tknretval == TEXT)
  604.             sprintf(error_msg,"%s%s%s","\nError: Invalid data, last opened element '",tp->nametoken,"'.\n");
  605.          else
  606.             sprintf(error_msg,"%s%s%s","\nError: Invalid tag, last opened element '",tp->nametoken,"'.\n");
  607.          FATAL_ERROR()
  608.       }
  609.  
  610.       /* check to make sure the element has content */
  611.       if (EMPTY_CONTENT(newcm)) {  /* can't have endtag for EMPTY */
  612.          token2 = token | HIGHBIT;
  613.          (*print_ctr)(ctrfp,"\n[/%s]",tp->nametoken);
  614.          (*applic)(END_TAG_NAME,tp->nametoken,"");
  615.       }
  616.       else
  617.          tknretval = gettoken(&tp,&token2,genthead,penthead,&dontcare);  /* must be end tag */
  618.       flush_buf();
  619.       if (IS_STARTTAG(token2) || IS_ENDTAG_NOTEQ(token2,token))
  620.          resolve_endtag(tp->cmptr,token2,tp,retval,tknretval,genthead,penthead,token);
  621.  
  622.       tknretval = gettoken(&tp,&token2,genthead,penthead,&dontcare);  /* must be end tag */
  623.       if (IS_STARTTAG(token2) || IS_ENDTAG_NOTEQ(token2,endtok))
  624.          resolve_endtag(tp->cmptr,token2,tp,retval,tknretval,genthead,penthead,endtok);
  625.       if (ptr != NULL)     /* don't decrement for root tag */
  626.          decroi(ptr); 
  627.  
  628.       popfree(newcm);  /* through with this content model */
  629.    }
  630.    else {
  631.       /* If endtag minimization was declared on the ELEMENT declaration */
  632.       opened_tp = lookstack();
  633.       if (opened_tp->miniexcept & ENDMINI_MASK) {
  634.          (*print_ctr)(ctrfp,"\n[/%s]",opened_tp->nametoken);
  635.          (*applic)(END_TAG_NAME,opened_tp->nametoken,"");
  636.          if (ptr != NULL)     /* don't decrement for root tag */
  637.             decroi(ptr); 
  638.          *retval = FOUND;
  639.          if (tknretval == MARKUP_FOUND)
  640.             ungettoken(token,tp);
  641.       }
  642.       else {
  643.          if (tknretval == TEXT)
  644.             sprintf(error_msg,"%s%s%s","\nError: Invalid data, last opened element '",opened_tp->nametoken,"'.\n");
  645.          else
  646.             sprintf(error_msg,"%s%s%s","\nError: Invalid tag, last opened element '",opened_tp->nametoken,"'.\n");
  647.          FATAL_ERROR()
  648.       }
  649.    }
  650.    return;
  651. }
  652.  
  653. /*------------------------------------------------------*/
  654. /*    P E R F O R M _ R O O T T A G _ M I N I   */
  655. /*      This routine attempts to perform starttag  */
  656. /*      minimization because a tag was encountered */
  657. /*      that was found in the symbol table but did */
  658. /*      not correspond to the expected tag.  In order */
  659. /* to minimize the tag, minimization must have  */
  660. /*      been enabled on the DTD element declaration   */
  661. /*      and the tag must be contextually required  */
  662. /* relative to the tags already encountered. */
  663. /*------------------------------------------------------*/
  664. STENTRY *perform_roottag_mini(roottoken)
  665. int roottoken;
  666. {
  667.    STENTRY *tp;
  668.  
  669.    if ((tp=linsrch(symtable,roottoken,numsym)) == NULL)
  670.       software_fault();
  671.  
  672.    /* Can't have the root element included from an exception */
  673.    if (tp->miniexcept & STARTMINI_MASK) { 
  674.       unprocess(tp->adptr);
  675.       (*print_ctr)(ctrfp,"\n[%s",tp->nametoken);
  676.       (*applic)(TAG_NAME,tp->nametoken,"");
  677.  
  678.       if (req_not_proc(tp->adptr) == TRUE) {
  679.          sprintf(error_msg,"%s%s%s","\nError: REQUIRED or CURRENT attribute not specified '",tp->adptr->attrname,"'.\n");
  680.          FATAL_ERROR()
  681.       }
  682.       if (resolve_attr(tp->adptr,TRUE) > GRPCNT)
  683.          ourexit(2,"\nError: Total number of idrefs and idreflist > GRPCNT\n");
  684.       (*put_ctr)(']',ctrfp);
  685.       (*applic)(TAG_END,"","");
  686.    }
  687.    else {
  688.       sprintf(error_msg,"%s%s%s","\nError: Invalid document element tag for '",tp->nametoken,"'.\n");
  689.       FATAL_ERROR()
  690.    }
  691.    return(tp);
  692. }
  693.  
  694. /*------------------------------------------------------*/
  695. /*           R E S O L V E _ S T A R T T A G          */
  696. /*      This routine attempts to perform starttag  */
  697. /*      minimization because a tag was encountered */
  698. /*      that was found in the symbol table but did */
  699. /*      not correspond to the expected tag.  In order */
  700. /* to minimize the tag, minimization must have  */
  701. /*      been enabled on the DTD element declaration   */
  702. /*      and the tag must be contextually required  */
  703. /* relative to the tags already encountered. */
  704. /*------------------------------------------------------*/
  705. void resolve_starttag(ptr,retval,genthead,penthead,tknretval,token)
  706. TNODE *ptr;
  707. STATUS *retval;
  708. ENTITYDESC *genthead,*penthead;
  709. TKNRETVAL tknretval;
  710. int token;
  711. {
  712.    STENTRY *tp;
  713.    int token2;  /* end tag token for exclusion handling */
  714.    TNODE *newcm;
  715.  
  716.    /* If the token that was read is a valid inclusion exception, you should
  717.       traverse the tree for that token as far as possible, then attempt to
  718.       resolve the missing starttag that got you here. */
  719.  
  720.    if (tknretval==MARKUP_FOUND && find_except(currincl,token) && !find_except(currexcl,token)) {
  721.       tknretval = gettoken(&tp,&token,genthead,penthead,&dontcare);
  722.       newcm = pushcreate(tp);
  723.  
  724.       if ((*retval = traverse(newcm,tp,genthead,penthead,&dontcare)) == NFSH) {
  725.          if (tknretval == TEXT)
  726.             sprintf(error_msg,"%s%s%s","\nError: Invalid data, last opened element '",tp->nametoken,"'.\n");
  727.          else
  728.             sprintf(error_msg,"%s%s%s","\nError: Invalid tag, last opened element '",tp->nametoken,"'.\n");
  729.          FATAL_ERROR()
  730.       }
  731.  
  732.       /* check to make sure the element has content */
  733.       if (EMPTY_CONTENT(newcm)) {   /* can't have endtag for EMPTY */
  734.          token2 = token | HIGHBIT;
  735.          putstr_outbuf("\n[/");
  736.          place_in_queue(END_TAG_NAME,tp->nametoken,"");
  737.          putstr_outbuf(tp->nametoken);
  738.          putstr_outbuf("]");
  739.       }
  740.       else
  741.          tknretval = gettoken(&tp,&token2,genthead,penthead,&dontcare);  /* must be end tag */
  742.  
  743.       flush_buf();
  744.       if (IS_STARTTAG(token2) || IS_ENDTAG_NOTEQ(token2,token))
  745.          resolve_endtag(tp->cmptr,token2,tp,retval,tknretval,genthead,penthead,token);
  746.       popfree(newcm);  /* through with this content model */
  747.    }
  748.    else
  749.       /*  The token must be contextually required and have starttag minimization
  750.          declared as legal on the ELEMENT declaration. */
  751.       if ((ptr->contreq!=C_NEVERO) && (symtable[ptr->nodeid].miniexcept & STARTMINI_MASK) &&
  752.           ptr->contreq!=C_SOMETIMESO && !find_except(currexcl,ptr->nodeid)) {
  753.          unprocess(symtable[ptr->nodeid].adptr);
  754.          ptr->contref_attr = FALSE;
  755.          (*print_ctr)(ctrfp,"\n[%s",symtable[ptr->nodeid].nametoken);
  756.          (*applic)(TAG_NAME,symtable[ptr->nodeid].nametoken,"");
  757.  
  758.          if (req_not_proc(symtable[ptr->nodeid].adptr) == TRUE) {
  759.             sprintf(error_msg,"%s%s%s","\nError: REQUIRED or CURRENT attribute not specified '",symtable[ptr->nodeid].adptr->attrname,"'.\n");
  760.             FATAL_ERROR()
  761.          }
  762.          if (resolve_attr(symtable[ptr->nodeid].adptr,TRUE) > GRPCNT)
  763.             ourexit(2,"\nError: Total number of idrefs and idreflist > GRPCNT\n");
  764.          if (symtable[ptr->nodeid].adptr == NULL)
  765.             (*put_ctr)(']',ctrfp);
  766.          else
  767.             (*print_ctr)(ctrfp,"\n]");
  768.          (*applic)(TAG_END,"","");
  769.          *retval = exe_new_conmod(&(symtable[ptr->nodeid]),genthead,penthead,ptr,TRUE);
  770.       }
  771.       else {
  772.          if (*retval != FOUND)  /* if FOUND once, stay FOUND forever */
  773.             *retval = (testoi(ptr) == OPT) ? NFDHT : NFSH;
  774.          NULLOI(ptr);  /* null so will know not to restore */
  775.       }
  776.    return;
  777. }
  778.  
  779. /*------------------------------------------------------*/
  780. /*        S O L V E A N D           */
  781. /*        Determines if the AND linked list should    */
  782. /*        resolve to NFDHT, FOUND, NFSH.  It       */
  783. /*        changes the retval to represent that.    */
  784. /*------------------------------------------------------*/
  785. void solveand(retval,currp,ptr)
  786. STATUS *retval;
  787. TNODE *currp,*ptr;
  788. {
  789.    switch(*retval) {
  790.    case NFDHT:
  791.       switch(testoi(currp)) {
  792.       case PLUS:  
  793.       case ONE:
  794.          *retval = NFSH;
  795.          break;
  796.       case OI_IS_NULL:
  797.          *retval = FOUND; 
  798.          break;
  799.       }
  800.       break;
  801.    case FOUND:
  802.       switch(testoi(currp)) {
  803.       case PLUS:  
  804.       case ONE:
  805.          *retval = NFSH;
  806.          break;
  807.       }  
  808.       break;
  809.    }
  810.    if (*retval==FOUND && testoi(ptr)==OPT)
  811.       incroi(ptr);
  812.    return;
  813. }
  814.  
  815. /*------------------------------------------------------*/
  816. /*               T R A V E R S E       */
  817. /*        Here we determine whether this node      */
  818. /*        is an operator, operand, or terminal.    */
  819. /*                   */
  820. /*           returns -- NFDHT, FOUND, NFSH      */
  821. /*------------------------------------------------------*/
  822. STATUS traverse(ptr,tp,genthead,penthead,firsttime)
  823. TNODE *ptr;
  824. STENTRY *tp;
  825. ENTITYDESC *genthead,*penthead;
  826. BOOLEAN *firsttime;
  827. {
  828.    register STATUS retval;
  829.  
  830.    /*   If the starttag had an explicit content reference attribute, its
  831.       content must be empty, so assume the traversal was successful and
  832.       return. */
  833.  
  834.    if (ptr->contref_attr == TRUE) {
  835.       retval = FOUND;
  836.       NULLOI(ptr);
  837.    }
  838.    else {
  839.       switch(ptr->nodeid) {
  840.       case COMMA:  
  841.       case AND:  
  842.       case OR:
  843.          retval = dooperator(ptr,tp,genthead,penthead);
  844.          break;
  845.       case CDATA:  
  846.       case RCDATA:  
  847.       case PCDATA: 
  848.       case EMPTY:
  849.          retval = doterminal(ptr,genthead,penthead);
  850.          break;
  851.       default:
  852.          retval = dooperand(ptr,tp,genthead,penthead);
  853.          break;
  854.       }
  855.       if (retval == FOUND)
  856.          *firsttime = TRUE;
  857.    }
  858.    return(retval);
  859. }
  860.